// @Author EthanScriptOn
// @Desc
package pool

import (
	"math"
	"math/rand"
	"time"
)

// SelectionWorkerFun How do I get a worker?
type SelectionWorkerFun func(map[Worker]struct{}) (worker Worker)

// SelectionWorkerMapping How to obtain the mapping of a worker
var SelectionWorkerMapping = map[WorkerSelection]SelectionWorkerFun{
	WEIGHT:      Weight,
	RANDOM:      Random,
	LOAD_FACTOR: LoadFactor,
}

// Weight Obtain a worker based on the weight
var Weight = func(workers map[Worker]struct{}) (worker Worker) {
	var maxWeightWorker Worker
	var maxWeight int
	for workerInstance := range workers {
		currentWeight := workerInstance.GetWeight()
		if currentWeight > maxWeight {
			maxWeight = currentWeight
			maxWeightWorker = workerInstance
		}
	}
	return maxWeightWorker
}

// Random Get a worker at random
var Random = func(workers map[Worker]struct{}) (worker Worker) {
	max := len(workers)
	if max <= 0 {
		return nil
	}
	rand.Seed(time.Now().UnixNano())
	max = rand.Intn(max)
	index := 0
	for workerInstance := range workers {
		if index == max {
			return workerInstance
		}
		index++
	}
	return nil
}

// LoadFactor Obtain a worker based on the load
var LoadFactor = func(workers map[Worker]struct{}) (worker Worker) {
	var minFactorWorker Worker
	var minFactor = math.MaxFloat64
	for workerInstance := range workers {
		currentLoadFactor := workerInstance.GetLoadFactor()
		if currentLoadFactor < minFactor {
			minFactor = currentLoadFactor
			minFactorWorker = workerInstance
		}
	}
	return minFactorWorker
}

// GetSelection Get the function to get the worker
func GetSelection(selection WorkerSelection) SelectionWorkerFun {
	workerFun, ok := SelectionWorkerMapping[selection]
	if !ok {
		return nil
	}
	return workerFun
}
